/*****************************************************************************************
   
   Copyright (c) Microsoft Corporation. All rights reserved.

   Use of this code sample is subject to the terms of the Microsoft
   Permissive License, a copy of which should always be distributed with
   this file.  You can also access a copy of this license agreement at:
   http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx

 ****************************************************************************************/

using System;

namespace GrooveWebServicesV12Helpers
{
	/// <summary>
	/// Provides access to a Groove workspace, its tools and members.
	/// </summary>
	public class Space : 
		GrooveWebServicesV12HelpersBase,
		GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback
	{
		private GrooveWebServicesV12Helpers.Identity m_Identity;

		private GrooveSpacesWebService.Space m_Space;
		private GrooveSpacesWebService.GrooveSpaces m_GrooveSpaces;

		internal static System.Collections.SortedList s_ToolEventManagerSortedList = new System.Collections.SortedList();
		internal static System.Collections.SortedList s_MemberEventManagerSortedList = new System.Collections.SortedList();
		
		private System.Collections.SortedList m_MemberSortedList;

		private int m_ToolEventManagerCount = 0;
		private int m_MemberEventManagerCount = 0;

		private string m_ToolSubscriptionID;
		private string m_MemberSubscriptionID;

		private GrooveWebServicesV12Helpers.Version m_Version;

		public enum Role
		{
			Guest = 0,
			Member = 1,
			Manager = 2
		}

		public Space(GrooveWebServicesV12Helpers.Identity i_Identity, GrooveSpacesWebService.Space i_Space)
			: base(i_Space.Name, i_Space.URI, i_Identity.URI, i_Identity.Context)
		{
			m_Identity = i_Identity;

			int_Space
				(
				i_Space
				);
		}

		public Space(GrooveSpacesWebService.Space i_Space, GrooveWebServicesV12Helpers.Context i_Context)
			: base(i_Space.Name, i_Space.URI, i_Space.IdentityURL, i_Context)
		{
			int_Space
				(
				i_Space
				);
		}

		public Space(string i_IdentityURL, GrooveSpacesWebService.Space i_Space, GrooveWebServicesV12Helpers.Context i_Context)
			: base(i_Space.Name, i_Space.URI, i_IdentityURL, i_Context)
		{
			int_Space
				(
				i_Space
				);
		}

		public void int_Space(GrooveSpacesWebService.Space i_Space)
		{
			m_Space = i_Space;
			
			m_MemberSortedList = new System.Collections.SortedList();
		}

		#region Space Properties And Methods

		public static GrooveSpacesWebService.GrooveSpaces CreateInitializedSpacesWebService(string i_IdentityURL, string i_TargetURI, GrooveWebServicesV12Helpers.Context i_Context)
		{
			//  Create the service
			GrooveSpacesWebService.GrooveSpaces SpacesWebService = new GrooveSpacesWebService.GrooveSpaces();

			//  Create the Groove SOAP header
			SpacesWebService.GrooveRequestHeaderValue = new GrooveSpacesWebService.GrooveRequestHeader();

			//  Set appropriate header values
			SpacesWebService.GrooveRequestHeaderValue.GrooveRequestKey = i_Context.RequestKey;
			SpacesWebService.GrooveRequestHeaderValue.GrooveIdentityURL = i_IdentityURL;

			//  Set the URL
			SpacesWebService.Url = i_Context.Host + i_TargetURI;

			//  Set Timeout
			SpacesWebService.Timeout = i_Context.Timeout;

			return SpacesWebService;
		}

		/// <summary>
		/// Sets the display name of the workspace.
		/// </summary>
		public override string Name
		{
			set
			{
				//  Do nothing if value already assigned
				if (m_Space.Name == value)
					return;

				string OldValue = m_Space.Name;

				try
				{
					//  Update local instance
					m_Space.Name = value;

					//  Update data
					int_Update();
				}
				catch (System.Exception Exception)
				{
					//  Revert local instance
					m_Space.Name = OldValue;

					throw Exception;
				}
			}
		}

		/// <summary>
		/// Gets the URI for raw access to the workspace via Local web service.
		/// </summary>
		public string LocalURI
		{
			get
			{
				return m_Space.LocalURI;
			}
		}

		/// <summary>
		/// Gets or sets the workspace description.
		/// </summary>
		public string Description
		{
			get
			{
				return m_Space.Description;
			}

			set
			{
				//  Do nothing if value already assigned
				if (m_Space.Description == value)
					return;

				string OldValue = m_Space.Description;

				try
				{
					//  Update local instance
					m_Space.Description = value;

					//  Update data
					int_Update();
				}
				catch (System.Exception Exception)
				{
					//  Revert local instance
					m_Space.Description = OldValue;

					throw Exception;
				}
			}
		}

		/// <summary>
		/// Gets the Identity of the current user of the workspace.
		/// </summary>
		public GrooveWebServicesV12Helpers.Identity Identity
		{
			get
			{
				//  If initialized without identity, we have to iterate through all accounts to 
				//  find one with matching identity URI
				if (m_Identity == null)
				{
					GrooveWebServicesV12Helpers.Account[] Accounts = GrooveWebServicesV12Helpers.Account.GetAccounts(Context);
					foreach (GrooveWebServicesV12Helpers.Account Account in Accounts)
					{
						foreach (GrooveWebServicesV12Helpers.Identity Identity in Account.Identities)
						{
							if (Identity.URI == this.URI)
							{
								m_Identity = Identity;
								break;
							}
						}
					}
				}

				return m_Identity;
			}
		}

		/// <summary>
		/// True if the workspace is present on the local device.
		/// </summary>
		public bool IsLocal
		{
			get
			{
				return m_Space.Local;
			}
		}

		/// <summary>
		/// The creation time of the workspace.
		/// </summary>
		public System.DateTime Created
		{
			get
			{
				return m_Space.Created;
			}
		}

		/// <summary>
		/// The modification time of the workspace.
		/// </summary>
		public System.DateTime Modified
		{
			get
			{
				return m_Space.Modified;
			}
		}

		/// <summary>
		/// Gets the array of Tools contained in the workspace.
		/// </summary>
		public GrooveWebServicesV12Helpers.Tool[] Tools
		{
			get
			{
				return int_GetToolsBySupportedTypes(null);
			}
		}

		/// <summary>
		/// Gets the workspace type.
		/// </summary>
		public string Type
		{
			get
			{
				return m_Space.Type;
			}
		}

		/// <summary>
		/// Gets the workspace version.
		/// </summary>
		public GrooveWebServicesV12Helpers.Version Version
		{
			get
			{
				if (m_Version == null)
				{
					m_Version = new GrooveWebServicesV12Helpers.Version
						(
						m_Space.Version.Major,
						m_Space.Version.Minor,
						m_Space.Version.Custom,
						m_Space.Version.Sequence
						);
				}

				return m_Version;
			}
		}

		/// <summary>
		/// True if there is unread data in the workspace.
		/// </summary>
		public bool IsUnread
		{
			get
			{
				return (m_Space.Unread > 0);
			}
		}

		/// <summary>
		/// Marks all data in the workspace as read.
		/// </summary>
		public void MarkRead()
		{
			//  Get initialized reference to web service
			GrooveSpacesWebService.GrooveSpaces SpacesWebService = this.GetInitializedSpacesWebService(this.URI);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to mark space read
			SpacesWebService.UpdateUnreadMark(false);

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("SpacesWebService::UpdateUnreadMark, " + TimeSpan.TotalMilliseconds + " ms");
			}
		}

		/// <summary>
		/// Marks all data in the workspace as unread.
		/// </summary>
		public void MarkUnread()
		{
			//  Get initialized reference to web service
			GrooveSpacesWebService.GrooveSpaces SpacesWebService = this.GetInitializedSpacesWebService(this.URI);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to mark space unread
			SpacesWebService.UpdateUnreadMark(true);

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("SpacesWebService::UpdateUnreadMark, " + TimeSpan.TotalMilliseconds + " ms");
			}
		}

		/// <summary>
		/// Updates the name and description of the workspace.
		/// </summary>
		/// <param name="i_Name">The new display name for the workspace.</param>
		/// <param name="i_Description">The new description for the workspace.</param>
		public void Update(string i_Name, string i_Description)
		{
			string Name_OldValue = i_Name;
			string Description_OldValue = i_Description;

			try
			{
				//  Update local instance
				m_Space.Name = i_Name;
				m_Space.Description = i_Description;

				//  Update data
				int_Update();
			}
			catch (System.Exception Exception)
			{
				//  Revert local instance
				m_Space.Name = Name_OldValue;
				m_Space.Description = Description_OldValue;

				throw Exception;
			}
		}

		private void int_Update()
		{
			//  Get initialized reference to web service
			GrooveSpacesWebService.GrooveSpaces SpacesWebService = this.GetInitializedSpacesWebService(this.URI);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to update Space entry
			SpacesWebService.Update(m_Space);

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("SpacesWebService::Update, " + TimeSpan.TotalMilliseconds + " ms");
			}
		}

		/// <summary>
		/// Exports the workspace and its contents as an archive.
		/// </summary>
		/// <param name="i_TargetFilePath">Specifies a file path on the local file system for the new archive file, for example, "C:\temp\archive.gsa".</param>
		public void ExportAsArchive(string i_TargetFilePath)
		{
			this.ExportAsArchive
				(
				i_TargetFilePath, 
				true,				//  IncludeMembers
				true,				//  IncludeData
				"",					//  Passphrase 
				true				//  ReadOnly
				);
		}

		/// <summary>
		/// Exports the workspace and its contents as an archive.
		/// </summary>
		/// <param name="i_TargetFilePath">Specifies a file path on the local file system for the new archive file, for example, "C:\temp\archive.gsa".</param>
		/// <param name="i_IncludeMembers">true if the list of workspace members is to be included in the archive.</param>
		public void ExportAsArchive(string i_TargetFilePath, bool i_IncludeMembers)
		{
			this.ExportAsArchive
				(
				i_TargetFilePath, 
				i_IncludeMembers,
				true,				//  IncludeData
				"",					//  Passphrase 
				true				//  ReadOnly
				);
		}

		/// <summary>
		/// Exports the workspace as an archive.
		/// </summary>
		/// <param name="i_TargetFilePath">Specifies a file path on the local file system for the new archive file, for example, "C:\temp\archive.gsa".</param>
		/// <param name="i_IncludeMembers">true if the list of workspace members is to be included in the archive.</param>
		/// <param name="i_IncludeData">true if the workspace contents are to be included in the archive.</param>
		public void ExportAsArchive(string i_TargetFilePath, bool i_IncludeMembers, bool i_IncludeData)
		{
			this.ExportAsArchive
				(
				i_TargetFilePath, 
				i_IncludeMembers,
				i_IncludeData,
				"",					//  Passphrase 
				true				//  ReadOnly
				);
		}

		/// <summary>
		/// Exports the workspace as an archive.
		/// </summary>
		/// <param name="i_TargetFilePath">Specifies a file path on the local file system for the new archive file, for example, "C:\temp\archive.gsa".</param>
		/// <param name="i_IncludeMembers">true if the list of workspace members is to be included in the archive.</param>
		/// <param name="i_IncludeData">true if the workspace contents are to be included in the archive.</param>
		/// <param name="i_Passphrase">Specifies a pass phrase to protect the archive.</param>
		public void ExportAsArchive(string i_TargetFilePath, bool i_IncludeMembers, bool i_IncludeData, string i_Passphrase)
		{
			this.ExportAsArchive
				(
				i_TargetFilePath, 
				i_IncludeMembers,
				i_IncludeData,
				i_Passphrase,
				true				//  ReadOnly
				);
		}

		/// <summary>
		/// Exports the workspace as an archive.
		/// </summary>
		/// <param name="i_TargetFilePath">Specifies a file path on the local file system for the new archive file, for example, "C:\temp\archive.gsa".</param>
		/// <param name="i_IncludeMembers">true if the list of workspace members is to be included in the archive.</param>
		/// <param name="i_IncludeData">true if the workspace contents are to be included in the archive.</param>
		/// <param name="i_Passphrase">Specifies a pass phrase to protect the archive.</param>
		/// <param name="i_ReadOnly">true to mark the archive file as read-only.</param>
		public void ExportAsArchive(string i_TargetFilePath, bool i_IncludeMembers, bool i_IncludeData, string i_Passphrase, bool i_ReadOnly)
		{
			//  Get initialized reference to web service
			GrooveSpacesWebService.GrooveSpaces SpacesWebService = this.GetInitializedSpacesWebService(this.URI);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to export space to GSA
			SpacesWebService.ExportAsArchive
				(
				i_TargetFilePath, 
				i_IncludeMembers, 
				i_IncludeData, 
				i_Passphrase, 
				i_ReadOnly
				);

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("SpacesWebService::ExportAsArchive, " + TimeSpan.TotalMilliseconds + " ms");
			}
		}

		/// <summary>
		/// Creates contents of an invitation file for the workspace.
		/// </summary>
		/// <param name="i_Role">Specifies the workspace role for the invitation as: "$Telespace.Manager", "$Telespace.Member", or "$Telespace.Guest".</param>
		/// <returns>String containing the invitation content.</returns>
		public string CreateInvitationFile(string i_Role)
		{
			//  Get initialized reference to web service
			GrooveSpacesWebService.GrooveSpaces SpacesWebService = this.GetInitializedSpacesWebService(this.URI);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to create an invitation file (given to us as a string of XML)
			string InvitationFileContent = SpacesWebService.CreateInvitationFile( i_Role );

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("SpacesWebService::CreateInvitationFile, " + TimeSpan.TotalMilliseconds + " ms");
			}

			return InvitationFileContent;
		}

		#endregion

		#region Tool Properties and Methods

		/// <summary>
		/// Returns the URI for raw web services access to the Tools service in this workspace.
		/// </summary>
		public string ToolsURI
		{
			get
			{
				return m_Space.Tools;
			}
		}

		/// <summary>
		/// Adds a new tool to the workspace.
		/// </summary>
		/// <param name="i_Name">Specifies the name of the new tool.</param>
		/// <param name="i_ToolTemplate">Specifies the template of the new tool. The list of tools is available with the ToolTemplates property.</param>
		/// <returns>URI of the new tool.</returns>
		public string AddTool(string i_Name, GrooveWebServicesV12Helpers.ToolTemplate i_ToolTemplate)
		{
			return AddTool
				(
				i_Name,
				i_ToolTemplate.ComponentResourceURL
				);
		}

		/// <summary>
		/// Adds a new tool to the workspace.
		/// </summary>
		/// <param name="i_Name">Specifies the name of the new tool.</param>
		/// <param name="i_ComponentResourceURL">Specifies the template of the new tool. The list of tools is available from the ToolTemplates property.</param>
		/// <returns>URI of the new tool.</returns>
		public string AddTool(string i_Name, string i_ComponentResourceURL)
		{
			//  Get initialized reference to web service
			GrooveToolsWebService.GrooveTools ToolsWebService = GrooveWebServicesV12Helpers.Tool.CreateInitializedToolsWebService
				(
				m_IdentityURL,
				m_Space.Tools,
				Context
				);

			//  Set parameters
			GrooveToolsWebService.Tool NewTool = new GrooveToolsWebService.Tool();
			NewTool.Name = i_Name;
			NewTool.ComponentResourceURL = i_ComponentResourceURL;

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to create tool
			string ToolURI = ToolsWebService.Create(NewTool);

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("ToolsWebService::Create, " + TimeSpan.TotalMilliseconds + " ms");
			}

			return ToolURI;
		}

		/// <summary>
		/// Gets a Tool in the workspace.
		/// </summary>
		/// <param name="i_ToolURI">URI of the tool.</param>
		/// <returns>a Tool.</returns>
		public GrooveWebServicesV12Helpers.Tool OpenTool(string i_ToolURI)
		{
			//  Get initialized reference to web service
			GrooveToolsWebService.GrooveTools ToolsWebService = GrooveWebServicesV12Helpers.Tool.CreateInitializedToolsWebService
				(
				m_IdentityURL,
				i_ToolURI,
				Context
				);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to read tool
			GrooveToolsWebService.Tool Tool = ToolsWebService.ReadTool();

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("ToolsWebService::ReadTool, " + TimeSpan.TotalMilliseconds + " ms");
			}

			GrooveWebServicesV12Helpers.Tool NewTool = int_OpenTool(Tool);
			return NewTool;
		}

		private GrooveWebServicesV12Helpers.Tool int_OpenTool(GrooveToolsWebService.Tool i_Tool)
		{
			GrooveWebServicesV12Helpers.Tool NewTool = null;

			switch (i_Tool.Type)
			{
                case GrooveWebServicesV12Helpers.Shared.GROOVE_SHAREPOINT_FILES_TOOL_SUPPORTED_TYPE:
                {
                    NewTool = new GrooveWebServicesV12Helpers.SharepointFiles.SharepointFilesTool
                        (
                        this,
                        i_Tool
                        );

                    break;
                }

				case GrooveWebServicesV12Helpers.Shared.GROOVE_FILES_TOOL_SUPPORTED_TYPE:
				{
                    NewTool = new GrooveWebServicesV12Helpers.Files.FilesTool
                        (
                        this,
                        i_Tool
                        );

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_DISCUSSION_TOOL_SUPPORTED_TYPE:
				{
					NewTool = new GrooveWebServicesV12Helpers.Discussion.DiscussionTool
						(
						this,
						i_Tool
						);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_CALENDAR_TOOL_SUPPORTED_TYPE:
				{
					NewTool = new GrooveWebServicesV12Helpers.Calendar.CalendarTool
						(
						this,
						i_Tool
						);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_FORMS_TOOL_SUPPORTED_TYPE:
				{
					NewTool = new GrooveWebServicesV12Helpers.Forms.FormsTool
						(
						this,
						i_Tool
						);

					break;
				}

                case GrooveWebServicesV12Helpers.Shared.GROOVE_FORMS2_TOOL_SUPPORTED_TYPE:
                {
                    NewTool = new GrooveWebServicesV12Helpers.Forms2.FormsTool
                        (
                        this,
                        i_Tool
                        );

                    break;
                }

                case GrooveWebServicesV12Helpers.Shared.GROOVE_INFOPATH_FORMS_TOOL_SUPPORTED_TYPE:
                {
                    NewTool = new GrooveWebServicesV12Helpers.InfoPathForms.InfoPathFormsTool
                        (
                        this,
                        i_Tool
                        );

                    break;
                }

				default:
				{
					NewTool = new GrooveWebServicesV12Helpers.Tool
						(
						this,
						i_Tool
						);

					break;
				}
			}

			return NewTool;
		}

		/// <summary>
		/// Gets an array of available ToolTemplates
		/// </summary>
		public GrooveWebServicesV12Helpers.ToolTemplate[] ToolTemplates
		{
			get
			{
				//  Get initialized reference to web service
				GrooveToolsWebService.GrooveTools ToolsWebService = GrooveWebServicesV12Helpers.Tool.CreateInitializedToolsWebService
					(
					m_IdentityURL,
					m_Space.Tools,
					Context
					);

				System.DateTime BeginDateTime = System.DateTime.Now;

				//  Ask service to get available tools
				GrooveToolsWebService.Template[] ToolTemplates = ToolsWebService.ReadAvailableTools();

				if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
				{
					System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
					System.Diagnostics.Debug.WriteLine("ToolsWebService::ReadAvailableTools, " + TimeSpan.TotalMilliseconds + " ms");
				}
				
				//  Create array list of tool templates
				System.Collections.ArrayList ToolTemplateArrayList = new System.Collections.ArrayList();
				foreach (GrooveToolsWebService.Template ToolTemplate in ToolTemplates)
				{
					GrooveWebServicesV12Helpers.ToolTemplate NewToolTemplate = new GrooveWebServicesV12Helpers.ToolTemplate(ToolTemplate);

					ToolTemplateArrayList.Add(NewToolTemplate);
				}

				//  Return array from array list
				return (GrooveWebServicesV12Helpers.ToolTemplate[])ToolTemplateArrayList.ToArray(typeof(GrooveWebServicesV12Helpers.ToolTemplate));
			}
		}

		/// <summary>
		/// Deletes a tool from the workspace.
		/// </summary>
		/// <param name="i_ToolURI">URI of the tool to delete.</param>
		public void RemoveTool(string i_ToolURI)
		{
			//  Get initialized reference to web service
			GrooveToolsWebService.GrooveTools ToolsWebService = GrooveWebServicesV12Helpers.Tool.CreateInitializedToolsWebService
				(
				m_IdentityURL,
				i_ToolURI,
				Context
				);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to create tool
			ToolsWebService.Delete();

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("ToolsWebService::Delete, " + TimeSpan.TotalMilliseconds + " ms");
			}
		}

		/// <summary>
		/// Gets an array of the tools in the workspace
		/// </summary>
		/// <param name="i_SupportedType">The supported type of tool to return, for example: "urn:groove.net:platform.tools.Files".</param>
		/// <returns>Array of Tools.</returns>
		public GrooveWebServicesV12Helpers.Tool[] GetToolsBySupportedType(string i_SupportedType)
		{
			return int_GetToolsBySupportedTypes(new string[]{i_SupportedType});
		}

		/// <summary>
		/// Gets an array of the tools in the workspace.
		/// </summary>
		/// <param name="i_SupportedType">The supported type of tool to return, for example: "urn:groove.net:platform.tools.Files".</param>
		/// <returns>Array of Tools.</returns>
		public GrooveWebServicesV12Helpers.Tool[] GetToolsBySupportedTypes(string[] i_SupportedTypes)
		{
			return int_GetToolsBySupportedTypes(i_SupportedTypes);
		}

		private GrooveWebServicesV12Helpers.Tool[] int_GetToolsBySupportedTypes(string[] i_SupportedTypes)
		{
			//  Get initialized reference to web service
			GrooveToolsWebService.GrooveTools ToolsWebService = GrooveWebServicesV12Helpers.Tool.CreateInitializedToolsWebService
				(
				m_IdentityURL,
				m_Space.Tools,
				Context
				);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to provide the list of tools for this space
			GrooveToolsWebService.Tool[] Tools = ToolsWebService.Read();

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("ToolsWebService::Read, " + TimeSpan.TotalMilliseconds + " ms");
			}

			//  Create array list of tools
			System.Collections.ArrayList ToolArrayList = new System.Collections.ArrayList();
			foreach (GrooveToolsWebService.Tool Tool in Tools)
			{
				//  If supported types specified, see if a match can be found
				//  for tool type
				if (i_SupportedTypes != null)
				{
					bool Found = false;

					foreach (string SupportedType in i_SupportedTypes)
					{
						if (SupportedType == Tool.Type)
						{
							Found = true;
							break;
						}
					}

					//  If match not found, just continue loop
					if (!Found)
						continue;
				}

				GrooveWebServicesV12Helpers.Tool NewTool = int_OpenTool(Tool);
				ToolArrayList.Add(NewTool);
			}

			//  Return array from array list
			return (GrooveWebServicesV12Helpers.Tool[])ToolArrayList.ToArray(typeof(GrooveWebServicesV12Helpers.Tool));
		}

		public string AddAggregatedToolEventListener(GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback i_IGrooveWebServicesEventCallback)
		{
			return this.int_AddAggregatedToolEventListener
				(
				GrooveWebServicesV12Helpers.Shared.GROOVE_WEB_SERVICES_TOOL_EVENT_CLASS,
				GrooveWebServicesV12Helpers.EventManager.s_EventURI,
				null,																	// Subscription ID
				"",																		// Query
				GrooveWebServicesV12Helpers.EventManager.DEFAULT_TIME_TO_LIVE_IN_HOURS,
				i_IGrooveWebServicesEventCallback
				);
		}

		/// <summary>
		///		Note that all string parameters are optional (i.e. use null for default values)
		/// </summary>
		public string AddAggregatedToolEventListener(string i_EventClass, string i_CallbackURI, string i_Query, GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback i_IGrooveWebServicesEventCallback)
		{
			string EventClass = !System.String.IsNullOrEmpty(i_EventClass) ? i_EventClass : GrooveWebServicesV12Helpers.Shared.GROOVE_WEB_SERVICES_TOOL_EVENT_CLASS;
			string CallbackURI = !System.String.IsNullOrEmpty(i_CallbackURI) ? i_CallbackURI : GrooveWebServicesV12Helpers.EventManager.s_EventURI;
			string Query = !System.String.IsNullOrEmpty(i_EventClass) ? i_Query : "";
				
			return this.int_AddAggregatedToolEventListener
				(
				EventClass,
				CallbackURI,
				null,
				Query,
				GrooveWebServicesV12Helpers.EventManager.DEFAULT_TIME_TO_LIVE_IN_HOURS,
				i_IGrooveWebServicesEventCallback
				);
		}

		/// <summary>
		///		Note that all string parameters are optional (i.e. use null for default values)
		/// </summary>
		public string AddAggregatedToolEventListener(string i_EventClass, string i_CallbackURI, string i_Query, int i_TimeToLive, GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback i_IGrooveWebServicesEventCallback)
		{
			string EventClass = !System.String.IsNullOrEmpty(i_EventClass) ? i_EventClass : GrooveWebServicesV12Helpers.Shared.GROOVE_WEB_SERVICES_TOOL_EVENT_CLASS;
			string CallbackURI = !System.String.IsNullOrEmpty(i_CallbackURI) ? i_CallbackURI : GrooveWebServicesV12Helpers.EventManager.s_EventURI;
			string Query = !System.String.IsNullOrEmpty(i_EventClass) ? i_Query : "";
				
			return this.int_AddAggregatedToolEventListener
				(
				EventClass,
				CallbackURI,
				null,
				Query,
				i_TimeToLive,
				i_IGrooveWebServicesEventCallback
				);
		}

		/// <summary>
		///		Note that all string parameters except i_SubscriptionID are optional (i.e. use null for default values)
		/// </summary>
		public void AddAggregatedToolEventListener(string i_EventClass, string i_CallbackURI, string i_Query, string i_SubscriptionID, GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback i_IGrooveWebServicesEventCallback)
		{
			if (System.String.IsNullOrEmpty(i_SubscriptionID))
				throw new System.ArgumentException("Invalid subscription id!");

			string EventClass = !System.String.IsNullOrEmpty(i_EventClass) ? i_EventClass : GrooveWebServicesV12Helpers.Shared.GROOVE_WEB_SERVICES_TOOL_EVENT_CLASS;
			string CallbackURI = !System.String.IsNullOrEmpty(i_CallbackURI) ? i_CallbackURI : GrooveWebServicesV12Helpers.EventManager.s_EventURI;
			string Query = !System.String.IsNullOrEmpty(i_EventClass) ? i_Query : "";
			
			this.int_AddAggregatedToolEventListener
				(
				EventClass,
				CallbackURI,
				i_SubscriptionID,
				Query,
				GrooveWebServicesV12Helpers.EventManager.DEFAULT_TIME_TO_LIVE_IN_HOURS,
				i_IGrooveWebServicesEventCallback
				);
		}

		/// <summary>
		///		Note that all string parameters except i_SubscriptionID are optional (i.e. use null for default values)
		/// </summary>
		public void AddAggregatedToolEventListener(string i_EventClass, string i_CallbackURI, string i_Query, string i_SubscriptionID, int i_TimeToLive, GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback i_IGrooveWebServicesEventCallback)
		{
			if (System.String.IsNullOrEmpty(i_SubscriptionID))
				throw new System.ArgumentException("Invalid subscription id!");

			string EventClass = !System.String.IsNullOrEmpty(i_EventClass) ? i_EventClass : GrooveWebServicesV12Helpers.Shared.GROOVE_WEB_SERVICES_TOOL_EVENT_CLASS;
			string CallbackURI = !System.String.IsNullOrEmpty(i_CallbackURI) ? i_CallbackURI : GrooveWebServicesV12Helpers.EventManager.s_EventURI;
			string Query = !System.String.IsNullOrEmpty(i_EventClass) ? i_Query : "";
			
			this.int_AddAggregatedToolEventListener
				(
				EventClass,
				CallbackURI,
				i_SubscriptionID,
				Query,
				i_TimeToLive,
				i_IGrooveWebServicesEventCallback
				);
		}

		private string int_AddAggregatedToolEventListener(string i_EventClass, string i_CallbackURI, string i_SubscriptionID, string i_Query, int i_TimeToLive, GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback i_IGrooveWebServicesEventCallback)
		{
			string SubscriptionID = i_SubscriptionID;

			if (System.String.IsNullOrEmpty(SubscriptionID))
			{
				SubscriptionID = GrooveWebServicesV12Helpers.EventManager.AddAggregatedEventListener
					(
					Context,
					m_IdentityURL,
					i_EventClass, 
					i_Query,
					this.URI,
					i_CallbackURI,
					i_TimeToLive,
					i_IGrooveWebServicesEventCallback
					);
			}
			else
			{
				GrooveWebServicesV12Helpers.EventManager.AddAggregatedEventListener
					(
					Context,
					m_IdentityURL,
					i_EventClass, 
					i_Query,
					this.URI,
					i_CallbackURI,
					SubscriptionID,
					i_TimeToLive,
					i_IGrooveWebServicesEventCallback
					);
			}

			return SubscriptionID;
		}

		public void RemoveAggregatedToolEventListener(string i_SubscriptionID, GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback i_IGrooveWebServicesEventCallback)
		{
			this.RemoveAggregatedToolEventListener
				(
				i_SubscriptionID,
				i_IGrooveWebServicesEventCallback,
				false
				);
		}

		public void RemoveAggregatedToolEventListener(string i_SubscriptionID, GrooveWebServicesV12Helpers.Shared.IGrooveWebServicesEventCallback i_IGrooveWebServicesEventCallback, bool i_PersistSubscription)
		{
			if (System.String.IsNullOrEmpty(i_SubscriptionID))
				throw new System.ArgumentException("Invalid subscription id!");

			GrooveWebServicesV12Helpers.EventManager.RemoveEventListener
				(
				i_SubscriptionID,
				i_IGrooveWebServicesEventCallback,
				i_PersistSubscription
				);
		}

		#region Tool Event Handling

		private void IncrementToolSubscriptionCount()
		{
			lock (s_ToolEventManagerSortedList)
			{
				if (m_ToolEventManagerCount == 0)
				{
					m_ToolSubscriptionID = GrooveWebServicesV12Helpers.EventManager.AddEventListener
						(
						Context,
						m_IdentityURL,
						GrooveWebServicesV12Helpers.Shared.GROOVE_WEB_SERVICES_TOOL_EVENT_CLASS, 
						this.ToolsURI,
						GrooveWebServicesV12Helpers.EventManager.s_EventURI,
						this
						);
				}

				++m_ToolEventManagerCount;
			}
		}

		private void DecrementToolSubscriptionCount()
		{
			lock (s_ToolEventManagerSortedList)
			{
				--m_ToolEventManagerCount;

				if (m_ToolEventManagerCount == 0)
				{
					GrooveWebServicesV12Helpers.EventManager.RemoveEventListener
						(
						m_ToolSubscriptionID,
						this
						);

					m_ToolSubscriptionID = null;
				}
			}
		}

		internal void RaiseToolAddedEvent(GrooveToolsWebService.Tool i_Tool)
		{
			if (InternalOnToolAddedEvent != null)
			{
				GrooveWebServicesV12Helpers.Tool Tool = int_OpenTool(i_Tool);
				InternalOnToolAddedEvent(Tool);
			}
		}

		internal void RaiseToolRenamedEvent(GrooveToolsWebService.Tool i_Tool)
		{
			if (InternalOnToolRenamedEvent != null)
			{
				GrooveWebServicesV12Helpers.Tool Tool = int_OpenTool(i_Tool);
				InternalOnToolRenamedEvent(Tool);
			}
		}

		internal void RaiseToolRemovedEvent(string i_ToolURI)
		{
			if (InternalOnToolRemovedEvent != null)
				InternalOnToolRemovedEvent(i_ToolURI);
		}

		public delegate void OnToolAddedDelegate(GrooveWebServicesV12Helpers.Tool i_Tool);
		internal event OnToolAddedDelegate InternalOnToolAddedEvent;
		/// <summary>
		/// Event raised when a tool is added to the workspace.
		/// </summary>
		public event OnToolAddedDelegate OnToolAddedEvent
		{
			add
			{
				IncrementToolSubscriptionCount();
				InternalOnToolAddedEvent += value;
			}
			remove
			{
				InternalOnToolAddedEvent -= value;
				DecrementToolSubscriptionCount();
			}
		}

		public delegate void OnToolRenamedDelegate(GrooveWebServicesV12Helpers.Tool i_Tool);
		internal event OnToolRenamedDelegate InternalOnToolRenamedEvent;
		/// <summary>
		/// Event raised when a tool in the workspace is renamed.
		/// </summary>
		public event OnToolRenamedDelegate OnToolRenamedEvent
		{
			add
			{
				IncrementToolSubscriptionCount();
				InternalOnToolRenamedEvent += value;
			}
			remove
			{
				InternalOnToolRenamedEvent -= value;
				DecrementToolSubscriptionCount();
			}
		}

		public delegate void OnToolRemovedDelegate(string i_ToolURI);
		internal event OnToolRemovedDelegate InternalOnToolRemovedEvent;
		/// <summary>
		/// Event raised when a tool in the workspace is deleted.
		/// </summary>
		public event OnToolRemovedDelegate OnToolRemovedEvent
		{
			add
			{
				IncrementToolSubscriptionCount();
				InternalOnToolRemovedEvent += value;
			}
			remove
			{
				InternalOnToolRemovedEvent -= value;
				DecrementToolSubscriptionCount();
			}
		}

		#endregion

		#endregion

		#region Member Properties, Methods and Events

		/// <summary>
		/// Returns the URI for raw web services access to the Members service in this workspace.
		/// </summary>
		public string MembersURI
		{
			get
			{
				return m_Space.Members;
			}
		}

		/// <summary>
		/// Gets an array of the workspace Members.
		/// </summary>
		public GrooveWebServicesV12Helpers.Member[] Members
		{
			get
			{
				//  Get initialized reference to web service
				GrooveMembersWebService.GrooveMembers MembersWebService = GrooveWebServicesV12Helpers.Member.CreateInitializedMembersWebService
					(
					m_IdentityURL,
					m_Space.Members,
					Context
					);

				System.DateTime BeginDateTime = System.DateTime.Now;

				//  Ask service to provide the list of spaces for this identity
				GrooveMembersWebService.Member[] Members = MembersWebService.Read();

				if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
				{
					System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
					System.Diagnostics.Debug.WriteLine("MembersWebService::Read, " + TimeSpan.TotalMilliseconds + " ms");
				}

				//  Create array list of Members
				System.Collections.ArrayList MemberArrayList = new System.Collections.ArrayList();
				foreach (GrooveMembersWebService.Member Member in Members)
				{
					GrooveWebServicesV12Helpers.Member NewMember = new GrooveWebServicesV12Helpers.Member 
						(
						this,
						Member
						);

					MemberArrayList.Add(NewMember);
				}

				//  Return array from array list
				return (GrooveWebServicesV12Helpers.Member[])MemberArrayList.ToArray(typeof(GrooveWebServicesV12Helpers.Member));
			}
		}

		/// <summary>
		/// Invites a contact to join the workspace.
		/// </summary>
		/// <param name="i_ContactURI">URI of the contact.</param>
		public void Invite(string i_ContactURI)
		{
			int_Invite
				(
				i_ContactURI,
				GrooveWebServicesV12Helpers.Space.Role.Member
				);
		}

		/// <summary>
		/// Invites one or more contacts to join the workspace.
		/// </summary>
		/// <param name="i_ContactURIs">Array of contact URIs.</param>
		public void Invite(string[] i_ContactURIs)
		{
			foreach (string ContactURI in i_ContactURIs)
			{
				int_Invite
					(
					ContactURI,
					GrooveWebServicesV12Helpers.Space.Role.Member
					);
			}
		}

		/// <summary>
		/// Invites a contact to join the workspace.
		/// </summary>
		/// <param name="i_ContactURI">URI of the contact.</param>
		/// <param name="i_Role">Specifies the workspace role for the invitation as: "$Telespace.Manager", "$Telespace.Member", or "$Telespace.Guest".</param>
		public void Invite(string i_ContactURI, GrooveWebServicesV12Helpers.Space.Role i_Role)
		{
			int_Invite
				(
				i_ContactURI,
				i_Role
				);
		}

		/// <summary>
		/// Incites one or more contacts to join the workspace.
		/// </summary>
		/// <param name="i_ContactURIs">Array of contact URIs.</param>
		/// <param name="i_Role">Specifies the workspace role for the invitation as: "$Telespace.Manager", "$Telespace.Member", or "$Telespace.Guest".</param>
		public void Invite(string[] i_ContactURIs, GrooveWebServicesV12Helpers.Space.Role i_Role)
		{
			foreach (string ContactURI in i_ContactURIs)
			{
				int_Invite
					(
					ContactURI,
					i_Role
					);
			}
		}

		private void int_Invite(string i_ContactURI, GrooveWebServicesV12Helpers.Space.Role i_Role)
		{
			//  Get initialized reference to web service
			GrooveMembersWebService.GrooveMembers MembersWebService = GrooveWebServicesV12Helpers.Member.CreateInitializedMembersWebService
				(
				m_IdentityURL,
				m_Space.Members,
				Context
				);

			GrooveMembersWebService.Member NewMember = new GrooveMembersWebService.Member();
			NewMember.URI = i_ContactURI;

			switch (i_Role)
			{
				case GrooveWebServicesV12Helpers.Space.Role.Guest:
				{
					NewMember.Role = "$Telespace.Guest";
					break;
				}

				case GrooveWebServicesV12Helpers.Space.Role.Member:
				{
					NewMember.Role = "$Telespace.Member";
					break;
				}

				case GrooveWebServicesV12Helpers.Space.Role.Manager:
				{
					NewMember.Role = "$Telespace.Manager";
					break;
				}

				default:
				{
					throw new System.ArgumentException("Unknown role: " + i_Role);
				}
			}

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to invite
			MembersWebService.Create(NewMember);

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("MembersWebService::Create, " + TimeSpan.TotalMilliseconds + " ms");
			}
		}

		/// <summary>
		/// Uninvites a workspace member.
		/// </summary>
		/// <param name="i_MemberURI">URI of the member.</param>
		public void Uninvite(string i_MemberURI)
		{
			Uninvite(new string[]{i_MemberURI});
		}

		/// <summary>
		/// Uninvites one or more workspace members.
		/// </summary>
		/// <param name="i_MemberURIs">Array of member URIs.</param>
		public void Uninvite(string[] i_MemberURIs)
		{
			//  Get initialized reference to web service
			GrooveMembersWebService.GrooveMembers MembersWebService = GrooveWebServicesV12Helpers.Member.CreateInitializedMembersWebService
				(
				m_IdentityURL,
				m_Space.Members,
				Context
				);

			System.DateTime BeginDateTime = System.DateTime.Now;

			//  Ask service to uninvite
			MembersWebService.Delete(i_MemberURIs);

			if (GrooveWebServicesV12Helpers.Context.TraceEnabled)
			{
				System.TimeSpan TimeSpan = System.DateTime.Now.Subtract(BeginDateTime);
				System.Diagnostics.Debug.WriteLine("MembersWebService::Delete, " + TimeSpan.TotalMilliseconds + " ms");
			}
		}

		private GrooveSpacesWebService.GrooveSpaces GetInitializedSpacesWebService(string i_TargetURI)
		{
            if ((m_GrooveSpaces == null) || Context.ReinitNeeded(m_GrooveSpaces.GrooveRequestHeaderValue.GrooveRequestKey))
			{
				m_GrooveSpaces = GrooveWebServicesV12Helpers.Space.CreateInitializedSpacesWebService
					(
					m_IdentityURL,
					i_TargetURI,
					Context
					);
			}
			else
			{
				//  Explicitly overwrite target URL and Timeout
				m_GrooveSpaces.Url = Context.Host + i_TargetURI;
                m_GrooveSpaces.Timeout = Context.Timeout;
			}

			return m_GrooveSpaces;
		}

		#region Member Event Handling

		private void IncrementMemberSubscriptionCount()
		{
			lock (s_MemberEventManagerSortedList)
			{
				if (m_MemberEventManagerCount == 0)
				{
					m_MemberSubscriptionID = GrooveWebServicesV12Helpers.EventManager.AddEventListener
						(
						Context,
						m_IdentityURL,
						GrooveWebServicesV12Helpers.Shared.GROOVE_WEB_SERVICES_MEMBER_EVENT_CLASS, 
						m_Space.Members,
						GrooveWebServicesV12Helpers.EventManager.s_EventURI,
						this
						);
				}

				++m_MemberEventManagerCount;
			}
		}

		private void DecrementMemberSubscriptionCount()
		{
			lock (s_MemberEventManagerSortedList)
			{
				--m_MemberEventManagerCount;

				if (m_MemberEventManagerCount == 0)
				{
					GrooveWebServicesV12Helpers.EventManager.RemoveEventListener
						(
						m_MemberSubscriptionID,
						this
						);

					m_MemberSubscriptionID = null;
				}
			}
		}

		internal void RaiseMemberAddedEvent(GrooveMembersWebService.Member i_Member)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnMemberAddedEvent != null)
			{
				GrooveWebServicesV12Helpers.Member Member = new GrooveWebServicesV12Helpers.Member
					(
					this,
					i_Member
					);

				InternalOnMemberAddedEvent(Member);
			}
		}
				
		internal void RaiseMemberUpdatedEvent(GrooveMembersWebService.Member i_Member)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnMemberUpdatedEvent != null)
			{
				GrooveWebServicesV12Helpers.Member Member = new GrooveWebServicesV12Helpers.Member
					(
					this,
					i_Member
					);

				InternalOnMemberUpdatedEvent(Member);
			}
		}

		internal void RaiseMemberRemovedEvent(string i_MemberURI)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnMemberRemovedEvent != null)
				InternalOnMemberRemovedEvent(i_MemberURI);
		}

		internal void RaiseMemberAwarenessChangedEvent(GrooveMembersWebService.Member i_Member)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnMemberAwarenessChangedEvent != null)
			{
				GrooveWebServicesV12Helpers.Member Member = new GrooveWebServicesV12Helpers.Member
					(
					this,
					i_Member
					);

				InternalOnMemberAwarenessChangedEvent(Member);
			}
		}

		internal void RaiseMemberEnteredSpaceEvent(GrooveMembersWebService.Member i_Member)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnMemberEnteredSpaceEvent != null)
			{
				GrooveWebServicesV12Helpers.Member Member = new GrooveWebServicesV12Helpers.Member
					(
					this,
					i_Member
					);

				InternalOnMemberEnteredSpaceEvent(Member);
			}
		}

		internal void RaiseMemberExitedSpaceEvent(GrooveMembersWebService.Member i_Member)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnMemberExitedSpaceEvent != null)
			{
				GrooveWebServicesV12Helpers.Member Member = new GrooveWebServicesV12Helpers.Member
					(
					this,
					i_Member
					);

				InternalOnMemberExitedSpaceEvent(Member);
			}
		}

		internal void RaiseMemberEnteredToolEvent(GrooveMembersWebService.Member i_Member)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnMemberEnteredToolEvent != null)
			{
				GrooveWebServicesV12Helpers.Member Member = new GrooveWebServicesV12Helpers.Member
					(
					this,
					i_Member
					);

				InternalOnMemberEnteredToolEvent(Member);
			}
		}

		internal void RaiseMemberExitedToolEvent(GrooveMembersWebService.Member i_Member)
		{
			//  If one or more subscribers, broadcast event
			if (InternalOnMemberExitedToolEvent != null)
			{
				GrooveWebServicesV12Helpers.Member Member = new GrooveWebServicesV12Helpers.Member
					(
					this,
					i_Member
					);

				InternalOnMemberExitedToolEvent(Member);
			}
		}

		public delegate void OnMemberAddedDelegate(GrooveWebServicesV12Helpers.Member i_Member);
		internal event OnMemberAddedDelegate InternalOnMemberAddedEvent;
		/// <summary>
		/// Event raised when a new member is added to the workspace.
		/// </summary>
		public event OnMemberAddedDelegate OnMemberAddedEvent
		{
			add
			{
				IncrementMemberSubscriptionCount();
				InternalOnMemberAddedEvent += value;
			}
			remove
			{
				InternalOnMemberAddedEvent -= value;
				DecrementMemberSubscriptionCount();
			}
		}

		public delegate void OnMemberUpdatedDelegate(GrooveWebServicesV12Helpers.Member i_Member);
		internal event OnMemberUpdatedDelegate InternalOnMemberUpdatedEvent;
		/// <summary>
		/// Event raised when a member is updated in the workspace.
		/// </summary>
		public event OnMemberUpdatedDelegate OnMemberUpdatedEvent
		{
			add
			{
				IncrementMemberSubscriptionCount();
				InternalOnMemberUpdatedEvent += value;
			}
			remove
			{
				InternalOnMemberUpdatedEvent -= value;
				DecrementMemberSubscriptionCount();
			}
		}

		public delegate void OnMemberRemovedDelegate(string i_MemberURI);
		internal event OnMemberRemovedDelegate InternalOnMemberRemovedEvent;
		/// <summary>
		/// Event raised when a member is removed from the workspace.
		/// </summary>
		public event OnMemberRemovedDelegate OnMemberRemovedEvent
		{
			add
			{
				IncrementMemberSubscriptionCount();
				InternalOnMemberRemovedEvent += value;
			}
			remove
			{
				InternalOnMemberRemovedEvent -= value;
				DecrementMemberSubscriptionCount();
			}
		}

		public delegate void OnMemberAwarenessChangedDelegate(GrooveWebServicesV12Helpers.Member i_Member);
		internal event OnMemberAwarenessChangedDelegate InternalOnMemberAwarenessChangedEvent;
		/// <summary>
		/// Event raised when a member's presence in the workspace changes.
		/// </summary>
		public event OnMemberAwarenessChangedDelegate OnMemberAwarenessChangedEvent
		{
			add
			{
				IncrementMemberSubscriptionCount();
				InternalOnMemberAwarenessChangedEvent += value;
			}
			remove
			{
				InternalOnMemberAwarenessChangedEvent -= value;
				DecrementMemberSubscriptionCount();
			}
		}

		public delegate void OnMemberEnteredSpaceDelegate(GrooveWebServicesV12Helpers.Member i_Member);
		internal event OnMemberEnteredSpaceDelegate InternalOnMemberEnteredSpaceEvent;
		/// <summary>
		/// Event raised when a member of the workspace enters the space.
		/// </summary>
		public event OnMemberEnteredSpaceDelegate OnMemberEnteredSpaceEvent
		{
			add
			{
				IncrementMemberSubscriptionCount();
				InternalOnMemberEnteredSpaceEvent += value;
			}
			remove
			{
				InternalOnMemberEnteredSpaceEvent -= value;
				DecrementMemberSubscriptionCount();
			}
		}

		public delegate void OnMemberExitedSpaceDelegate(GrooveWebServicesV12Helpers.Member i_Member);
		internal event OnMemberExitedSpaceDelegate InternalOnMemberExitedSpaceEvent;
		/// <summary>
		/// Event raised when a member of the workspace exits the space.
		/// </summary>
		public event OnMemberExitedSpaceDelegate OnMemberExitedSpaceEvent
		{
			add
			{
				IncrementMemberSubscriptionCount();
				InternalOnMemberExitedSpaceEvent += value;
			}
			remove
			{
				InternalOnMemberExitedSpaceEvent -= value;
				DecrementMemberSubscriptionCount();
			}
		}

		public delegate void OnMemberEnteredToolDelegate(GrooveWebServicesV12Helpers.Member i_Member);
		internal event OnMemberEnteredToolDelegate InternalOnMemberEnteredToolEvent;
		/// <summary>
		/// Event raised when a member of the workspace enters a tool in the space.
		/// </summary>
		public event OnMemberEnteredToolDelegate OnMemberEnteredToolEvent
		{
			add
			{
				IncrementMemberSubscriptionCount();
				InternalOnMemberEnteredToolEvent += value;
			}
			remove
			{
				InternalOnMemberEnteredToolEvent -= value;
				DecrementMemberSubscriptionCount();
			}
		}

		public delegate void OnMemberExitedToolDelegate(GrooveWebServicesV12Helpers.Member i_Member);
		internal event OnMemberExitedToolDelegate InternalOnMemberExitedToolEvent;
		/// <summary>
		/// Event raised when a member of the workspace exits a tool in the space.
		/// </summary>
		public event OnMemberExitedToolDelegate OnMemberExitedToolEvent
		{
			add
			{
				IncrementMemberSubscriptionCount();
				InternalOnMemberExitedToolEvent += value;
			}
			remove
			{
				InternalOnMemberExitedToolEvent -= value;
				DecrementMemberSubscriptionCount();
			}
		}

		#endregion

		#endregion

		#region GrooveWebServicesV12HelpersBase Implementation

		public override string BindableURL
		{
			get
			{
				if (System.String.IsNullOrEmpty(m_BindableURL))
				{
					//  Sample Space URI is '/GWS/Groove/2.0/Spaces/grooveTelespace/t9r5q57hwpmb4gwy5aju7vg4czgt3jepsvtqqsi', so we strip
					//  off '/GWS/Groove/2.0/Spaces/grooveTelespace/'
					string SpaceInfo = this.URI;
					int Index = SpaceInfo.LastIndexOf("/") + 1;
					SpaceInfo = SpaceInfo.Substring(Index);

					m_BindableURL = m_Identity.BindableURL + "/Space#" + SpaceInfo;
				}

				return m_BindableURL;
			}
		}

		public override object BindToObject(ref string[] i_URLParts, int i_URLPartIndex)
		{
			object BindResult = null;
			string RelativeURL = i_URLParts[i_URLPartIndex];
			string[] RelativeURLParts = RelativeURL.Split(new char[]{'#'});

			string ObjectType = RelativeURLParts[0];

			if (ObjectType == "Tool")
			{
				string ToolURI = this.URI + "/ToolContainer/" + RelativeURLParts[1];
				ToolURI = ToolURI.Replace("Spaces", "Tools");

				try
				{
					BindResult = this.OpenTool(ToolURI);
				}
				catch (System.Exception Exception)
				{
					throw new System.Exception("Unable to find Tool '" + ToolURI +"' (" + Exception.Message + ")");
				}

				//  If there are more url parts, continue binding
				if (i_URLParts.Length > ++i_URLPartIndex)
				{
					GrooveWebServicesV12Helpers.IBindServices IBindServices = (GrooveWebServicesV12Helpers.IBindServices)BindResult;
					BindResult = IBindServices.BindToObject(ref i_URLParts, i_URLPartIndex);
				}
			}
			else
				throw new System.Exception("Unknown space url part - '" + ObjectType + "'");

			return BindResult;
		}

		#endregion
	
		#region IGrooveWebServicesEventCallback Members

		public void ProcessEvent(GrooveEventsWebService.Event i_Event)
		{
			switch (i_Event.EventType)
			{
				case GrooveWebServicesV12Helpers.Shared.GROOVE_TOOL_ADD_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveToolAddEventData ToolAddEventData = (GrooveEventsWebService.GrooveToolAddEventData)i_Event.EventData;
					this.RaiseToolAddedEvent(ToolAddEventData.Tool);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_TOOL_DELETE_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveToolDeleteEventData ToolDeleteEventData = (GrooveEventsWebService.GrooveToolDeleteEventData)i_Event.EventData;
					this.RaiseToolRemovedEvent(ToolDeleteEventData.ToolURI);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_TOOL_RENAME_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveToolRenameEventData ToolRenameEventData = (GrooveEventsWebService.GrooveToolRenameEventData)i_Event.EventData;
					this.RaiseToolRenamedEvent(ToolRenameEventData.Tool);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_MEMBER_ADD_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveMemberAddEventData MemberAddEventData = (GrooveEventsWebService.GrooveMemberAddEventData)i_Event.EventData;
					this.RaiseMemberAddedEvent(MemberAddEventData.Member);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_MEMBER_UPDATE_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveMemberUpdateEventData MemberUpdateEventData = (GrooveEventsWebService.GrooveMemberUpdateEventData)i_Event.EventData;
					this.RaiseMemberUpdatedEvent(MemberUpdateEventData.Member);
										
					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_MEMBER_DELETE_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveMemberDeleteEventData MemberDeleteEventData = (GrooveEventsWebService.GrooveMemberDeleteEventData)i_Event.EventData;
					this.RaiseMemberRemovedEvent(MemberDeleteEventData.MemberURI);

					break;
					}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_MEMBER_AWARENESS_CHANGED_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveMemberAwarenessEventData MemberAwarenessEventData = (GrooveEventsWebService.GrooveMemberAwarenessEventData)i_Event.EventData;
					this.RaiseMemberAwarenessChangedEvent(MemberAwarenessEventData.Member);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_MEMBER_AWARENESS_ENTER_SPACE_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveMemberEnterSpaceEventData MemberEnterSpaceEventData = (GrooveEventsWebService.GrooveMemberEnterSpaceEventData)i_Event.EventData;
					this.RaiseMemberEnteredSpaceEvent(MemberEnterSpaceEventData.Member);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_MEMBER_AWARENESS_EXIT_SPACE_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveMemberExitSpaceEventData MemberExitSpaceEventData = (GrooveEventsWebService.GrooveMemberExitSpaceEventData)i_Event.EventData;
					this.RaiseMemberExitedSpaceEvent(MemberExitSpaceEventData.Member);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_MEMBER_AWARENESS_ENTER_TOOL_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveMemberEnterToolEventData MemberEnterToolEventData = (GrooveEventsWebService.GrooveMemberEnterToolEventData)i_Event.EventData;
					this.RaiseMemberEnteredToolEvent(MemberEnterToolEventData.Member);

					break;
				}

				case GrooveWebServicesV12Helpers.Shared.GROOVE_MEMBER_AWARENESS_EXIT_TOOL_EVENT_TYPE:
				{
					GrooveEventsWebService.GrooveMemberExitToolEventData MemberExitToolEventData = (GrooveEventsWebService.GrooveMemberExitToolEventData)i_Event.EventData;
					this.RaiseMemberExitedToolEvent(MemberExitToolEventData.Member);

					break;
				}

				default:
				{
					System.Diagnostics.Debug.WriteLine("[Space]Unknown event type: " + i_Event.EventType);
					break;
				}
			}
		}

		#endregion
	}
}
